﻿using System;
using System.Xml;
using System.Collections.Generic;
using System.Windows.Forms;
using SdmxMl.Helper;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.GenericMetadata;
using SdmxMl.Multiplexor;


namespace SdmxMl.Manager
{
    public static class NavigationManager
    {
        private static TreeView tv;
        private static ToolStripButton butBack;
        private static ToolStripButton butFwd;
        private static ToolStripButton butParent;
        private static ToolStripComboBox comboHistory;


        private static int historySize;
        private static int currentIndex;
        private static List<object> objList;
        private static bool ownRequest;


        static NavigationManager()
        {
            historySize = 10;
            objList = new List<object>(historySize);
            currentIndex = -1;

        }

        public static TreeView TreeSdmx
        {
            set
            {
                if (tv != null)
                {
                    tv.AfterSelect -= new TreeViewEventHandler(treeView_AfterSelect);
                }

                // New one
                tv = value;
                tv.AfterSelect += new TreeViewEventHandler(treeView_AfterSelect);
            }
        }

        private static void treeView_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (ownRequest)
            {
                UpdatenavigationState();
                ownRequest = false;
                return;
            }
            if (e != null && e.Node != null && e.Node.Tag != null)
            {
                if (currentIndex + 1 == historySize)
                {
                    objList.RemoveAt(0);
                    objList.Add(e.Node.Tag);
                }
                else
                {
                    objList.Add(e.Node.Tag);
                  //  ++currentIndex;
                    currentIndex = objList.Count - 1;
                }

                UpdatenavigationState();
            }
        }

        public static void Reset()
        {
            objList.Clear();
            currentIndex = -1;
            if (tv != null && tv.SelectedNode != null)
            {
                objList.Add(tv.SelectedNode.Tag);
                currentIndex = 0;
            }
            
            UpdatenavigationState();
        }

        public static void RecordNavButtons(ToolStripButton bBack, ToolStripButton bFwd, 
            ToolStripButton bParent, ToolStripComboBox cbHistory)
        {
            butBack = bBack;
            butFwd = bFwd;
            butParent = bParent;
            comboHistory = cbHistory;

            butBack.Click += new System.EventHandler(toolStripButtonNavBack_Click);
            butFwd.Click += new System.EventHandler(toolStripButtonNavFwd_Click);
            butParent.Click += new System.EventHandler(toolStripButtonNavParent_Click);
            comboHistory.SelectedIndexChanged += new EventHandler(comboHistory_SelectedIndexChanged);

            UpdatenavigationState();

        }

        static void comboHistory_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboHistory.SelectedIndex != currentIndex)
            {
                TreeNode targetNode = null;
                TreeHelper.LocateObject(tv, objList[comboHistory.SelectedIndex], ref targetNode);
                if (targetNode != null)
                {
                    currentIndex = comboHistory.SelectedIndex;
                    ownRequest = true;
                    tv.SelectedNode = targetNode;
                }
            }
        }

        public static void RequestNavigation(object ob)
        {
            if (ob != null)
            {
                TreeNode targetNode = null;
                if (tv != null)
                {
                    TreeHelper.LocateObject(tv, ob, ref targetNode);
                    if (targetNode != null)
                    {
                        tv.SelectedNode = targetNode;
                        // PRA to explore in metaset
                        targetNode.Expand();
                    }
                }
            }
       }


        private static void toolStripButtonNavBack_Click(object sender, EventArgs e)
        {
            TreeNode targetNode = null;
            if (currentIndex > 0 && tv != null && tv.SelectedNode != null)
            {
                TreeHelper.LocateObject(tv, objList[currentIndex-1], ref targetNode);
                if (targetNode != null)
                {
                    if (currentIndex >= 0)
                        --currentIndex;
                    ownRequest = true;
                    tv.SelectedNode = targetNode;
                }

            }
        }

        private static void UpdatenavigationState()
        {
            if (butBack != null)
            {
                butBack.Enabled = currentIndex > 0;
                butFwd.Enabled = currentIndex + 1 < objList.Count;
                butParent.Enabled = true;
                comboHistory.Items.Clear();
                comboHistory.Items.AddRange(objList.ToArray());
                if (currentIndex >= 0 && currentIndex < comboHistory.Items.Count)
                    comboHistory.SelectedIndex = currentIndex;
            }

        }

        private static void toolStripButtonNavFwd_Click(object sender, EventArgs e)
        {
            TreeNode targetNode = null;
            if (currentIndex + 1 < objList.Count && tv != null && tv.SelectedNode != null)
            {
                TreeHelper.LocateObject(tv, objList[currentIndex+1], ref targetNode);
                if (targetNode != null)
                {
                    ++currentIndex;
                    ownRequest = true;
                    tv.SelectedNode = targetNode;
                }

            }

        }

        private static void toolStripButtonNavParent_Click(object sender, EventArgs e)
        {
            TreeNode targetNode = null;
            if (tv != null && tv.SelectedNode != null)
            {
                targetNode = TreeHelper.GetParentArtefactNode(tv.SelectedNode);
                if (targetNode == null)
                {
                    if (tv.SelectedNode.Parent != null)
                        targetNode = tv.SelectedNode.Parent;
                }
            }

            if (targetNode != null)
               tv.SelectedNode = targetNode;
        }

        public static object LoadFromSmManager(SmManager sm)
        {
            object ob = null;
            foreach (TreeNode node in tv.Nodes)
            {
                ArtefactManager mgr = node.Tag as ArtefactManager;
                if (mgr != null)
                {
                    ArtefactManager arSm = sm.GetArtefactManager(mgr.ArtefactType);
                    if (arSm != null)
                    {
                        bool modified = false;
                        foreach (Artefact a in arSm.ArtefactList)
                        {
                            if (mgr.Exists(a) == false)
                            {
                                mgr.ArtefactList.Add(a);
                                ob = a;
                                modified = true;
                            }
                        }
                        if (modified)
                        {
                            node.Nodes.Clear();
                            mgr.FillNode(node, 0);
                        }
                    }
                }
            }

            return ob;
        }

        public static object LocatenavigateProject(SdmxArtefactType aType, string fullId, UrnParser p)
        {
            object ob = null;

            // If already in memory, navigate
            ArtefactManager am = ReferenceManager.LocateArtefactManager(aType);
            if (am != null)
            {
                ob = am.GetFromFullId(fullId);
                if (p == null || string.IsNullOrEmpty(p.SubType))
                    RequestNavigation(ob);
                else
                {
                    object ochild = SubClassNavigate(ob, p);
                    if (ochild != null)
                        ob = ochild;

                    RequestNavigation(ob);
                    // now in complex detail view, select corresponding element
                    if (ReferenceManager.TreeMgr.PanelDetail != null)
                        ReferenceManager.TreeMgr.PanelDetail.SetSelectedItemFromUrn(p);
                }
            }
            return ob;
        }

        public static object LocateNavigateWithRegistry(SdmxArtefactType aType, string fullId, bool loadRef, bool navigate, UrnParser p)
        {
            object ob = null;

            // If already in memory, navigate
            ArtefactManager am = ReferenceManager.LocateArtefactManager(aType);
            if (am != null)
            {
                using (new SdmxMl.Helper.CWaitCursor())
                {
                    try
                    {
                        SmManager sm = new SmManager();
                        Artefact a = am.GetFromFullId(fullId);
                        if (a == null)
                        {
                            // Try to obtain from Registry
                            if (Mux.Instance.ExistArtefact(aType.ToString(), fullId))
                            {
                                XmlDocument xDoc = Mux.Instance.GetArtefact(aType.ToString(), fullId);
                                sm.LoadSdmxDocument(null, true, xDoc);
                                SdmxMl.Common.ArtefactRefTyped art = new SdmxMl.Common.ArtefactRefTyped(aType, fullId);
                                ob = a = sm.GetArtefact(art);
                                if (loadRef)
                                    sm.LoadMissingArtefactsFromRegistry(a.ArtefactRefList);

                                LoadFromSmManager(sm);
                            }
                        }
                        else
                        {
                            sm.PutArtefact(aType, a);
                            ob = a;
                            if (loadRef)
                                sm.LoadMissingArtefactsFromRegistry(a.ArtefactRefList);

                            LoadFromSmManager(sm);
                        }


                        if (navigate)
                        {
                            if (p == null || string.IsNullOrEmpty(p.SubType))
                                RequestNavigation(ob);
                            else
                            {
                                object ochild = SubClassNavigate(ob, p);
                                if (ochild != null)
                                    ob = ochild;

                                RequestNavigation(ob);

                                // now in complex detail view, select corresponding element
                                if (ReferenceManager.TreeMgr.PanelDetail != null)
                                    ReferenceManager.TreeMgr.PanelDetail.SetSelectedItemFromUrn(p);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Unable to navigate to " + fullId + ". Reason:"
                            + Environment.NewLine + ex.Message, "Caution");

                    }

                }
            }
            return ob;
        }

        public static void NavigateWithRegistry(SdmxArtefactType aType, string fullId, bool loadRef, UrnParser p)
        {
            LocateNavigateWithRegistry(aType, fullId, loadRef, true, p);

        }

        private static object SubClassNavigate(object oParent, UrnParser p)
        {
            object o = null;
            Artefact ar = oParent as Artefact;
            if (ar != null && p != null && string.IsNullOrEmpty(p.SubType) == false)
            {
                string subType = p.SubType.ToLower();
                if (subType == "code")
                {
                    CodeListType clt = ar as CodeListType;
                    if (clt != null)
                        o = clt.CodeList.LocateItem(p.SubTypeId);
                }

                else if (subType == "concept")
                {
                    ConceptSchemeType cst = ar as ConceptSchemeType;
                    if (cst != null)
                        o = cst.ConceptList.LocateItem(p.SubTypeId);
                }
                else if (subType == "category")
                {
                    CategorySchemeType cst = ar as CategorySchemeType;
                    if (cst != null)
                        o = cst.CategoryList.LocateItemFromFullId(p.SubTypeId, cst.CategoryList);
                }
                else if (subType == "dimension")
                {
                    KeyFamilyType kft = ar as KeyFamilyType;
                    if (kft != null)
                        o = kft.DimensionList;
                }
                else if (subType == "attribute")
                {
                    KeyFamilyType kft = ar as KeyFamilyType;
                    if (kft != null)
                        o = kft.AttributeList;
                }
                else if (subType == "crosssectionalmeasure")
                {
                    KeyFamilyType kft = ar as KeyFamilyType;
                    if (kft != null)
                        o = kft.CrossSectionalMeasureList;
                }
                else if (subType == "reportstructure")
                {
                    MetadataStructureDefinitionType msd = ar as MetadataStructureDefinitionType;
                    if (msd != null)
                    {
                        string[] arrR = p.SubTypeId.Split('.');
                        foreach (ReportStructureType rpt in msd.ReportStructureList)
                        {
                            if (rpt.Id == arrR[0])
                            {
                                o = rpt;
                                break;
                            }
                        }
                    }
                }
                else if (subType == "hierarchy")
                {
                    HierarchicalCodelistType hclt = ar as HierarchicalCodelistType;
                    if (hclt != null)
                    {
                        foreach (HierarchyType ht in hclt.HierarchyList)
                        {
                            if (ht.Id == p.SubTypeId)
                            {
                                o = ht;
                                break;
                            }
                        }
                    }
                }
                else if (subType == "codelistref")
                {
                    HierarchicalCodelistType hclt = ar as HierarchicalCodelistType;
                    o = hclt.CodelistRefList;
                }

                else if (subType == "reportedattributetype")
                {
                    MetadataSetType mdst = ar as MetadataSetType;
                    if (mdst != null)
                    {
                        string[] arrR = p.SubTypeId.Split('.');
                        if (arrR[0] == mdst.ReportRef)
                        {
                            string fullRowId = ComposeAttributeValueSetFullId(arrR);
                            foreach (AttributeValueSetType at in mdst.AttributeValueSetList)
                            {
                                if (at.ToString() == fullRowId)
                                {
                                    o = at;
                                    break;
                                }
                            }
                        }
                    }
                }

                else if (subType == "targetvalues")
                {
                    MetadataSetType mdst = ar as MetadataSetType;
                    if (mdst != null && mdst.AttributeValueSetList.Count > 0)
                    {
                        string[] arrR = p.SubTypeId.Split('.');
                        if (arrR[0] == mdst.AttributeValueSetList[0].TargetRef)
                        {
                            foreach (AttributeValueSetType at in mdst.AttributeValueSetList)
                            {
                                if (SameMetaKey(arrR, at))
                                {
                                    o = at;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
                return o;
        }

        /// <summary>
        /// Check if target values similar to one in array given from Url
        /// </summary>
        /// <returns>True if matchs</returns>
        private static bool SameMetaKey(string[] arr, AttributeValueSetType at)
        {
            bool found = false;
            if (at.TargetValuesTypeList.Count == arr.Length - 1)
            {
                int i = 1;
                foreach (ComponentValueType cvt in at.TargetValuesTypeList)
                {
                    if (cvt.Value != arr[i++])
                        return false;
                }
                found = true;
            }
            return found;
        }

        private static string ComposeAttributeValueSetFullId(string[] arr)
        {
            string fid = string.Empty;
            if (arr.Length > 2)
            {
                System.Text.StringBuilder sb = new System.Text.StringBuilder();
                for (int i = 1; i < arr.Length - 1; ++i)
                {
                    sb.Append(arr[i]);
                    if (i < arr.Length - 2)
                    sb.Append('.');
                }
                fid = sb.ToString();
            }
            
            return fid;
        }

    }
}
